#ifndef QUERYDATAMANIPULATION_H
#define QUERYDATAMANIPULATION_H

#include <vector>
#include <list>
#include <functional>

#define ResultVector std::vector<DataIterator>
#define SelectVector std::vector<SelectType>

#define WhereFunction std::function<bool (const DataIterator&)>
#define OrderByFunction std::function<bool (const DataIterator&, const DataIterator&)>
#define SelectFunction std::function<SelectType (const DataIterator&)>
#define ExecuteFunction std::function<void (ResultVector&, int)>

namespace QDM
{
	template<typename DataIterator, typename SelectType = DataIterator> class IKernel
	{
	public:
		virtual ~IKernel(){};

		virtual void where(ResultVector& resultVector, WhereFunction isRequired) const = 0;
		virtual void orderBy(ResultVector& resultVector, OrderByFunction isLarger) const = 0;
		virtual void select(const ResultVector& resultVector, SelectVector& resultSelect, SelectFunction selectFunction) const = 0;
		virtual void execute(ResultVector&, ExecuteFunction) const = 0;
	};



	template<typename DataIterator, typename SelectType = DataIterator> class QDMResult
	{
	public:
		SelectVector select(SelectFunction selectFunction) const
		{
			SelectVector resultSelect = SelectVector();

			_kernel->select(_resultVector, resultSelect, selectFunction);

			return std::move(resultSelect);
		}

		void execute(ExecuteFunction executeFunction)
		{
			_kernel->execute(_resultVector, executeFunction);
		}

		const ResultVector& result() const
		{
			return _resultVector;
		}

		void setKernel(const IKernel<DataIterator, SelectType>* kernel)
		{
			_kernel = kernel;
		}

	protected:
		QDMResult(const IKernel<DataIterator, SelectType>* kernel) : _kernel(kernel){}
		~QDMResult()
		{
		}

		const IKernel<DataIterator, SelectType>* _kernel;
		ResultVector _resultVector;
	};


	template<typename DataIterator, typename SelectType = DataIterator> class QDMOrderBy : public QDMResult<DataIterator, SelectType>
	{
	public:
		QDMResult<DataIterator, SelectType>& orderBy(OrderByFunction isLarger)
		{
			_kernel->orderBy(_resultVector, isLarger);

			QDMResult* result = this;

			return *this;
		}

	protected:
		QDMOrderBy(const IKernel<DataIterator, SelectType>* kernel) : QDMResult(kernel){}
	};


	template<typename DataIterator, typename SelectType = DataIterator> class QDMWhere : public QDMOrderBy<DataIterator, SelectType>
	{
	public:
		QDMOrderBy<DataIterator, SelectType>& where(WhereFunction isRequired)
		{
			_kernel->where(_resultVector, isRequired);

			QDMOrderBy* result = this;

			return *this;
		}

	protected:
		QDMWhere(const IKernel<DataIterator, SelectType>* kernel) : QDMOrderBy(kernel){}
	};


	template<typename DataIterator, typename SelectType = DataIterator> class QueryDataManipulation : public QDMWhere<DataIterator, SelectType>
	{
	public:
		QueryDataManipulation(const IKernel<DataIterator, SelectType>* kernel) : QDMWhere(kernel){}

		QDMWhere& from(const DataIterator& begin, const DataIterator& end)
		{
			_resultVector.clear();

			for (DataIterator iterator = begin; iterator != end; iterator++)
			{
				_resultVector.push_back(iterator);
			}

			QDMWhere* result = this;

			return *result;
		}

		QDMWhere& from(const DataIterator& begin, size_t size)
		{
			_resultVector.resize(size);

			DataIterator iterator = begin;

			for (size_t i = 0; i < size; i++)
			{
				_resultVector[i] = iterator;
				iterator++;
			}

			QDMWhere* result = this;

			return *result;
		}
	};
}

#endif